package com.digital.welcome.websocket.mywebsocket;

import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Future;

import javax.websocket.server.ServerEndpoint;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriBuilder;
import org.yaml.snakeyaml.events.Event.ID;

import com.digital.welcome.util.ApplicationContextUtils;
import com.digital.welcome.websocket.mywebsocket.model.WebSocketMessage;
import com.digital.welcome.websocket.mywebsocket.model.WebSocketMessageEvent;
import com.digital.welcome.websocket.mywebsocket.model.WebSocketMessageType;

import cn.hutool.core.date.DateTime;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.websocket.*;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@ServerEndpoint(value = "/websocket/inject")
@Component
public class WebSocketSession implements IWebSocketSession {

    public WebSocketMessageService wsmService = (WebSocketMessageService) ApplicationContextUtils
            .getBean(WebSocketMessageService.class);


    private Long ID;

    @Override
    public Long GetWebSocketId() {
        return ID;
    }

    /**
     * 连接建立成功调用的方法
     * @throws IOException
     */
    @OnOpen
    public void onOpen(Session session) throws IOException {
        try {
            ArrayList<WebSocketMessage> messages = new ArrayList<>();
            ID = injectWebSocket(session);
            messages.add(0, new WebSocketMessage(
                    WebSocketMessageEvent.OnConnect,
                    WebSocketMessageType.Text,
                    ID.toString()));

            ArrayList<WebSocketMessage> sendMessages = wsmService.onMessage(this, messages);
            if (sendMessages == null)
                return;
            SendMessage(session, sendMessages);
        } catch (Exception e) {
            log.error("Failed To Open The Connection Caused By: \n");
            session.close();
            e.printStackTrace();
        }

    }

    /**
     * 连接关闭调用的方法
     * 
     * @throws IOException
     */
    @OnClose
    public void onClose(Session session) throws IOException {
        disposeWebSocket(session);

        ArrayList<WebSocketMessage> messages = new ArrayList<WebSocketMessage>();
        messages = new ArrayList<WebSocketMessage>();
        try {
            messages.add(new WebSocketMessage(
                    WebSocketMessageEvent.OnDisconnect,
                    WebSocketMessageType.Text,
                    "Client Had Disconnected"));

            wsmService.onMessage(this, messages);
        } catch (Exception e) {
            e.printStackTrace();
            session.close();
        }

        return;
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @throws IOException
     * @throws Exception
     */
    @OnMessage
    public void onMessage(byte[] bytes, Session session) throws IOException {
        try {

            ArrayList<WebSocketMessage> messages = new ArrayList<>();
            WebSocketMessage.getWebSocketMessages(bytes, messages, 0);
            WebSocketMessage firstMessage = messages.get(0);

            if (firstMessage != null && firstMessage.getMessageEvent().Code == WebSocketMessageEvent.OnConnect.Code) {
                messages.add(0, new WebSocketMessage(
                        WebSocketMessageEvent.OnConnect,
                        WebSocketMessageType.Text,
                        ID.toString()));
            }

            ArrayList<WebSocketMessage> sendMessages = wsmService.onMessage(this, messages);
            if (sendMessages == null || sendMessages.size() == 0)
                return;
            SendMessage(session, sendMessages);

        } catch (Exception e) {
            log.error("Failed To Handle The Message Caused By: \n");
            e.printStackTrace();
            session.close();
        }

        // WebSocketMessage firstMessage = message.get(0);
        // log.info("服务端收到客户端[{}]的消息:{}", session.getId(), message);
        // this.sendMessage(message);
    }

    @OnError
    public void onError(Session session, Throwable error) {

        if (error.getClass() == EOFException.class) {
            ArrayList<WebSocketMessage> messages = new ArrayList<WebSocketMessage>();
            messages = new ArrayList<WebSocketMessage>();
            try {
                messages.add(new WebSocketMessage(
                        WebSocketMessageEvent.OnDisconnect,
                        WebSocketMessageType.Text,
                        "Client Had Disconnected"));

                wsmService.onMessage(this, messages);
            } catch (Exception e) {
                e.printStackTrace();
            }

            return;
        } else {
            log.error("Failed To Receive The Message Or Get WebSocketMessages From Messages");
            error.printStackTrace();
        }

    }

    @Override
    public Future SendMessage(long webSocketID, ArrayList<WebSocketMessage> sendMessages) throws Exception {
        Session clientWebSocket = wsmService.getWebSocketServerCollection().get(webSocketID);
        if (clientWebSocket == null) {
            log.error("[{}] : Cannot Find Client WebSocket By WebSocket ID {}, Execute Default Task", DateTime.now(),
                    webSocketID);
            return null;
        } else {
            log.debug("[{}] : Send To Client {}", DateTime.now(), webSocketID);
            return SendMessage(clientWebSocket, sendMessages);
        }

    }

    public Future SendMessage(Session session, ArrayList<WebSocketMessage> sendMessages) throws Exception {
        byte[] sendBytes = WebSocketMessage.setBytes(sendMessages);
        return session.getAsyncRemote().sendBinary(ByteBuffer.wrap(sendBytes));

    }

    @Override
    public void ClientClose(long webSocketId, CloseReason reason) {
        if (wsmService.getWebSocketServerCollection().get(webSocketId) != null) {
            log.error("[{}] : Cannot Find Client WebSocket By WebSocket ID {}", DateTime.now(), webSocketId);
            return;
        }
        try {
            Session clientWebSocket = wsmService.getWebSocketServerCollection().get(webSocketId);
            clientWebSocket.close(reason);
        } catch (Exception e) {
            log.error("Closed WebSocket {} Abnormally", webSocketId);
        }
    }

    private Long injectWebSocket(Session session) {
        
        Long ID = new Random().nextLong();
        wsmService.getWebSocketServerCollection().put(ID, session);
        log.debug("[{}] : WebSocket Client {{ id = {} }} Has Connected, WebSocket Collection Count : {}",
                DateTime.now(), ID, wsmService.getWebSocketServerCollection().size());
        return ID;
    }

    private void disposeWebSocket(Session session) throws IOException {
        Long[] ID = new Long[1];
        wsmService.getWebSocketServerCollection().forEach((id, socket) -> {
            if (socket == session) {
                ID[0] = id;
            }
        });

        wsmService.getWebSocketServerCollection().get(ID[0]).close();
        wsmService.getWebSocketServerCollection().remove(ID[0]);
        log.debug("[{}] : WebSocket Client {{ id = {} }} Has Disposed, WebSocket Collection Count : {}", DateTime.now(),
                ID, wsmService.getWebSocketServerCollection().size());
    }

}
